#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>

#include "imbus_internal.h"
#include "im_array.h"
#include "im_object_array.h"

#define PREALLOC_BLOCK_SIZE     16

/************************** Declaration of internal functions ***************/
static void     __im_object_array_initialize    (IMPointer      array);
static void     __im_object_array_finalize      (IMPointer      array);
static void     __im_object_array_copy          (IMPointer      dest,
                                                 IMConstPointer src);
static IMBool   __im_object_array_serialize     (IMConstPointer obj,
                                                 IMPointer      stream);
static IMBool   __im_object_array_deserialize   (IMPointer      obj,
                                                 IMPointer      stream);
static void     __im_object_array_class_init    (IMPointer      klass);

/****************** Declaration of object and class structure ************/
struct _IMObjectArray
{
    IMObject object;

    /**< private >**/
    IMPtrArray  *array;
};

struct _IMObjectArrayClass
{
    IMObjectClass parent_class;
};

/************************ Internal varable ************************/
static const IMObjectClass *__im_object_array_parent_class = 0;

/************* Implementation of internal functions ***************/
static void
__im_object_array_initialize (IMPointer array)
{
    ((IMObjectArray*)array)->array = im_ptr_array_new ();
}

static void
__im_object_array_finalize (IMPointer array)
{
    IMObjectArray *ap = IM_OBJECT_ARRAY (array);
    IMSize i;

    _im_assert (ap != 0);

    for (i=0; i<im_ptr_array_size (ap->array); ++i)
        if (im_ptr_array_index(ap->array, i))
            im_object_unref (im_ptr_array_index (ap->array, i));

    im_ptr_array_destroy (ap->array, TRUE);
}

static void
__im_object_array_copy (IMPointer dest, IMConstPointer src)
{
    IMObjectArray       *dap = IM_OBJECT_ARRAY (dest);
    const IMObjectArray *sap = IM_CONST_OBJECT_ARRAY (src);

    _im_assert (dap != 0 && sap != 0);

    im_object_array_clear (dap);

    if (im_ptr_array_size (sap->array)) {
        IMSize i;

        im_ptr_array_reserve (dap->array, im_ptr_array_size (sap->array));

        for (i=0; i<im_ptr_array_size (sap->array); ++i) {
            if (im_ptr_array_index (sap->array, i))
                im_ptr_array_append (dap->array,
                    im_object_ref (im_object_clone (im_ptr_array_index (sap->array, i))));
            else
                im_ptr_array_append (dap->array, 0);
        }
    }

    /* Call copy method of parent class */
    __im_object_array_parent_class->copy (dest, src);
}

static IMBool
__im_object_array_serialize (IMConstPointer obj, IMPointer stream)
{
    const IMObjectArray *ap = IM_CONST_OBJECT_ARRAY (obj);
    IMStream            *sp = IM_STREAM (stream);
    IMSize               i;

    _im_assert (ap != 0 && sp != 0);

    /* Call parent serialize method */
    if (!__im_object_array_parent_class->serialize (obj, stream))
        return FALSE;
    if (!im_stream_put_uint32 (sp, im_ptr_array_size (ap->array)))
        return FALSE;
    for (i = 0; i < im_ptr_array_size (ap->array); ++i)
        if (!im_stream_put_object (sp, im_ptr_array_index (ap->array, i)))
            return FALSE;

    return TRUE;
}

static IMBool
__im_object_array_deserialize (IMPointer obj, IMPointer stream)
{
    IMObjectArray   *ap = IM_OBJECT_ARRAY (obj);
    IMStream        *sp = IM_STREAM (stream);
    IMPointer        val;
    IMType           type;
    IMSize           i;
    IMUInt32         n = 0;

    _im_assert (ap != 0 && sp != 0);

    /* Call parent deserialize method */
    if (!__im_object_array_parent_class->deserialize (obj, stream))
        return FALSE;

    if (im_stream_get_data_type (sp) != IM_TYPE_UINT32 || !im_stream_get_uint32 (sp, &n)) 
        return FALSE;

    im_object_array_clear (ap);

    im_ptr_array_reserve (ap->array, n);

    for (i = 0; i < n; ++i) {
        type = im_stream_get_data_type (sp);
        if (!IM_TYPE_IS_OBJECT(type))
            return FALSE;
        /* Newly created object should be owned by the IMObjectArray object.*/
        val = im_object_ref (im_object_new (type));
        if (!val) {
            _im_warn ("Failed to create object for type id %d, name %s\n", type, im_type_get_name (type));
            return FALSE;
        }
        if (!im_stream_get_object (sp, val)) {
            im_object_unref (val);
            return FALSE;
        }
        im_ptr_array_append (ap->array, val);
    }

    return TRUE;
}

static void
__im_object_array_class_init (IMPointer klass)
{
    IMObjectClass *cp = (IMObjectClass *)klass;

    cp->copy        = __im_object_array_copy;
    cp->serialize   = __im_object_array_serialize;
    cp->deserialize = __im_object_array_deserialize;

    __im_object_array_parent_class = im_object_class_get_parent (cp);
}

/************* Implementation of public functions ***************/
void
__im_type_register_object_array ()
{
    static IMTypeInfo im_object_array_type_info = 
    {
        sizeof (IMObjectArrayClass), /**< class_size >**/
        __im_object_array_class_init,/**< class_init >**/
        0,                           /**< class_finalize >**/

        sizeof (IMObjectArray),      /**< instance_size >**/
        __im_object_array_initialize,/**< instance_init >**/
        __im_object_array_finalize,  /**< instance_finalize >**/
    };

    __im_type_register_builtin_class (IM_TYPE_OBJECT,
                                      IM_TYPE_OBJECT_ARRAY,
                                      "ObjectArray",
                                      &im_object_array_type_info,
                                      TRUE);
}

IMObjectArray*
im_object_array_new ()
{
    IMObjectArray *a = (IMObjectArray *) im_object_new (IM_TYPE_OBJECT_ARRAY);

    _im_assert (a);

    return a;
}

void
im_object_array_clear (IMObjectArray *array)
{
    IMSize i;
    _im_return_if_fail (IM_IS_OBJECT_ARRAY (array));

    for (i=0; i<im_ptr_array_size (array->array); ++i)
        if (im_ptr_array_index(array->array, i))
            im_object_unref (im_ptr_array_index (array->array, i));

    im_ptr_array_clear (array->array);
}

void
im_object_array_reserve (IMObjectArray *array, IMSize space)
{
    _im_return_if_fail (IM_IS_OBJECT_ARRAY (array));

    im_ptr_array_reserve (array->array, space);
}

void
im_object_array_free_unused_memory (IMObjectArray  *array)
{
    _im_return_if_fail (IM_IS_OBJECT_ARRAY (array));

    im_ptr_array_free_unused_memory (array->array);
}

IMSize
im_object_array_size (const IMObjectArray *array)
{
    _im_return_val_if_fail (IM_IS_OBJECT_ARRAY (array), 0);

    return im_ptr_array_size (array->array);
}

IMPointer
im_object_array_get (const IMObjectArray *array, IMSize index)
{
    _im_return_val_if_fail (IM_IS_OBJECT_ARRAY (array), 0);

    return (index < im_ptr_array_size (array->array)) ? im_ptr_array_index (array->array, index) : 0;
}

void
im_object_array_set (IMObjectArray *array, IMSize index, IMPointer object)
{
    _im_return_if_fail (IM_IS_OBJECT_ARRAY (array));
    _im_return_if_fail (IM_IS_OBJECT (object));
    _im_return_if_fail (index < im_ptr_array_size (array->array));

    im_object_ref (object);
    im_object_unref (im_ptr_array_index(array->array, index));

    im_ptr_array_index(array->array, index) = object;
}

IMObjectArray*
im_object_array_prepend (IMObjectArray *array, IMPointer object)
{
    _im_return_val_if_fail (IM_IS_OBJECT_ARRAY (array), 0);
    _im_return_val_if_fail (IM_IS_OBJECT (object), 0);

    im_object_ref (object);

    im_ptr_array_prepend (array->array, object);

    return array;
}

IMObjectArray*
im_object_array_append (IMObjectArray *array, IMPointer object)
{
    _im_return_val_if_fail (IM_IS_OBJECT_ARRAY (array), 0);
    _im_return_val_if_fail (IM_IS_OBJECT (object), 0);

    im_object_ref (object);

    im_ptr_array_append (array->array, object);

    return array;
}

IMObjectArray*
im_object_array_insert (IMObjectArray *array, IMSize index, IMPointer object)
{
    _im_return_val_if_fail (IM_IS_OBJECT_ARRAY (array), 0);
    _im_return_val_if_fail (IM_IS_OBJECT (object), 0);
    _im_return_val_if_fail (index <= im_ptr_array_size (array->array), 0);

    im_object_ref (object);

    im_ptr_array_insert (array->array, index, object);

    return array;
}

IMObjectArray*
im_object_array_remove (IMObjectArray *array, IMSize index)
{
    IMPointer object;

    _im_return_val_if_fail (IM_IS_OBJECT_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_ptr_array_size (array->array), 0);

    object = im_ptr_array_remove (array->array, index);

    if (object) im_object_unref (object);

    return array;
}

IMObjectArray*
im_object_array_remove_fast (IMObjectArray *array, IMSize index)
{
    IMPointer object;

    _im_return_val_if_fail (IM_IS_OBJECT_ARRAY (array), 0);
    _im_return_val_if_fail (index < im_ptr_array_size (array->array), 0);

    object = im_ptr_array_remove_fast (array->array, index);

    if (object) im_object_unref (object);

    return array;
}
void
im_object_array_sort (IMObjectArray *array, IMCompareFunc compare_func)
{
    _im_return_if_fail (IM_IS_OBJECT_ARRAY (array));
    _im_return_if_fail (compare_func);

    im_ptr_array_sort (array->array, compare_func);
}

void
im_object_array_sort_with_data (IMObjectArray *array, IMCompareDataFunc compare_func, IMPointer user_data)
{
    _im_return_if_fail (IM_IS_OBJECT_ARRAY (array));
    _im_return_if_fail (compare_func);

    im_ptr_array_sort_with_data (array->array, compare_func, user_data);
}

IMSize
im_object_array_lower_bound (const IMObjectArray *array, IMPointer obj, IMCompareFunc compare_func)
{
    _im_return_val_if_fail (IM_IS_OBJECT_ARRAY (array), 0);
    _im_return_val_if_fail (IM_IS_OBJECT (obj), 0);
    _im_return_val_if_fail (compare_func, 0);

    return im_ptr_array_lower_bound (array->array, obj, compare_func);
}

IMSize
im_object_array_lower_bound_with_data (const IMObjectArray *array, IMPointer obj, IMCompareDataFunc compare_func, IMPointer user_data)
{
    _im_return_val_if_fail (IM_IS_OBJECT_ARRAY (array), 0);
    _im_return_val_if_fail (IM_IS_OBJECT (obj), 0);
    _im_return_val_if_fail (compare_func, 0);

    return im_ptr_array_lower_bound_with_data (array->array, obj, compare_func, user_data);
}

IMSize
im_object_array_upper_bound (const IMObjectArray *array, IMPointer obj, IMCompareFunc compare_func)
{
    _im_return_val_if_fail (IM_IS_OBJECT_ARRAY (array), 0);
    _im_return_val_if_fail (IM_IS_OBJECT (obj), 0);
    _im_return_val_if_fail (compare_func, 0);

    return im_ptr_array_upper_bound (array->array, obj, compare_func);
}

IMSize
im_object_array_upper_bound_with_data (const IMObjectArray *array, IMPointer obj, IMCompareDataFunc compare_func, IMPointer user_data)
{
    _im_return_val_if_fail (IM_IS_OBJECT_ARRAY (array), 0);
    _im_return_val_if_fail (IM_IS_OBJECT (obj), 0);
    _im_return_val_if_fail (compare_func, 0);

    return im_ptr_array_upper_bound_with_data (array->array, obj, compare_func, user_data);
}
void
im_object_array_foreach (const IMObjectArray *array, IMFunc func, IMPointer user_data)
{
    _im_return_if_fail (IM_IS_OBJECT_ARRAY (array));
    _im_return_if_fail (func);

    im_ptr_array_foreach (array->array, func, user_data);
}


/*
vi:ts=4:nowrap:ai:expandtab
*/
